# 25. 继承


# 继承

继承(英语:inheritance)是面向对象软件技术当中的一个概念。

如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。

继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。

另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。

继承是面向对象的三大特性之一



# 为什么要有继承

  1. 优化代码
  2. 节省代码
  3. 提高代码的复用性
  4. 提高代码的维护性
  5. 让类与类之间发生关系

# 初始继承

  1. 子类以及子类实例化的对象,可以访问父类的任何方法跟变量
  2. 类名可以访问父类所有内容
  3. 子类实例化的对象也可以访问父类所有内容

父类:就是在类中要引用的类就叫父类,也可以叫基类,超类

子类:就是引用类的原先类本身就叫子类,也可以叫派生类

查询顺序:使用继承会先对自己类中进行查询,如果查询不到才会去指定的父类中进行查询


接下来看一个简单的继承例子

class admin:
    def __init__(self,name,sex,age):
        self.name = name
        self.sex = sex
        self.age = age

class eo(admin):
    pass

class wo(admin):
    pass

class so(admin):
    pass

ss = so("江凡","男",22)
print(so.__dict__)

执行结果:
{'name': '江凡', 'sex': '男', 'age': 22}

​ 以上实例,对so类进行实验化过程,但是在so类中并没有__init__方法,那么就会去指定好的父类中进行实例化过程


# 子类父类同时引用

父类一般都是用于存储公共区的数据,方便类去引用,那要怎么样才能同时引用父类跟子类

在实例化过程中如果子类拥有__init__方法那么程序是不会去父类获取数据的

那就要实现子类中有__init__方法又可以调用父类中的__init__方法

有二种方法可以实现,但是一般都是使用第二种方法


# 第一种

第一种是使用父类名.__init__方法来将子类传输的一些参数传递过去并执行生成对象空间

比如动物的叫声都是不一样的 ,那么就可以用叫声来区别开

class admin:
    def __init__(self,name,sex,age,):
        self.name = name
        self.sex = sex
        self.age = age

class dog(admin):
    def __init__(self,name,sex,age,cry):
        admin.__init__(self,name,sex,age)
        print("%s她是%s,今年%s岁,她老是%s叫" %(self.name,self.sex,self.age,cry))


class cat(admin):
    def __init__(self, name, sex, age, cry):
        admin.__init__(self, name, sex, age)
        print("%s他是%s,今年%s岁,他老是%s叫" %(self.name,self.sex,self.age,cry))

class bird(admin):
    def __init__(self, name, sex, age, cry):
        admin.__init__(self, name, sex, age)
        print("%s她是%s,今年%s岁,她老是%s叫" %(self.name,self.sex,self.age,cry))

bird = bird("一只可爱的小鸟","母的",2,"吱吱")
cat = cat("一只威猛的蓝猫","公的",2,"喵喵")
dog = dog("一只温顺的金毛","母的",2,"旺旺")

执行结果:
一只可爱的小鸟她是母的,今年2岁,她老是吱吱叫
一只威猛的蓝猫他是公的,今年2岁,他老是喵喵叫
一只温顺的金毛她是母的,今年2岁,她老是旺旺叫

# 第二种(推荐)

第二种是使用super().__init__方法来将子类传输的一些参数传递过去并执行生成对象空间

一般默认是使用这种方式

注意: 使用super()的时候不用传递对象空间的地址,就是不用传递self值

比如动物的叫声都是不一样的 ,那么就可以用叫声来区别开

class admin:
    def __init__(self,name,sex,age,):
        self.name = name
        self.sex = sex
        self.age = age

class dog(admin):
    def __init__(self,name,sex,age,cry):
        super().__init__(name,sex,age)
        print("%s她是%s,今年%s岁,她老是%s叫" %(self.name,self.sex,self.age,cry))


class cat(admin):
    def __init__(self, name, sex, age, cry):
        super().__init__(name,sex,age)
        print("%s他是%s,今年%s岁,他老是%s叫" %(self.name,self.sex,self.age,cry))

class bird(admin):
    def __init__(self, name, sex, age, cry):
        super().__init__(name,sex,age)
        print("%s她是%s,今年%s岁,她老是%s叫" %(self.name,self.sex,self.age,cry))

bird = bird("一只可爱的小鸟","母的",2,"吱吱")
cat = cat("一只威猛的蓝猫","公的",2,"喵喵")
dog = dog("一只温顺的金毛","母的",2,"旺旺")

执行结果:
一只可爱的小鸟她是母的,今年2岁,她老是吱吱叫
一只威猛的蓝猫他是公的,今年2岁,他老是喵喵叫
一只温顺的金毛她是母的,今年2岁,她老是旺旺叫

以上实例中使用的super()是使用简写方式的

super(子类名,self).__init__(要传递的参数)
class admin:
    def __init__(self,name,sex,age,):
        self.name = name
        self.sex = sex
        self.age = age

class dog(admin):
    def __init__(self,name,sex,age,cry):
        super(dog,self).__init__(name,sex,age)
        print("%s她是%s,今年%s岁,她老是%s叫" %(self.name,self.sex,self.age,cry))
dog = dog("一只温顺的金毛","母的",2,"旺旺")

执行结果:
一只温顺的金毛她是母的,今年2岁,她老是旺旺叫


# 类的分类

到了继承又引出了类的分类,为什么不写在23章中,因为类的分类是以继承来区分的

类:

  1. 经典类:不继承object类的都是经典类
  2. 新式类:继承object类的都是新式类

注意:

  1. python3中的所有类都是新式类,因为python3解释器会默认自动给类中都继承object类
  2. 在python2中,即有新式类,又有经典类,因为python2解释器不会给类中自动继承object类,如果想让类为新式类,就要手动去写继承上object类,所有的类都默认都是经典类


# 继承进阶

继承也分类:单继承跟多继承

单继承基本就是以上的内容,接下来是多继承的内容


# 多继承

class A:
    def so():
        print(1)

class B:
    pass

class C(B,A):
    pass

C.so()

执行结果:
1

​ 多继承写法跟单继承差不多,不过多个父类使用 , 逗号分隔



# 多继承查询分类

这才是多继承的重点

多继承查询:

  1. 新式类:遵循广度优先
    1. 经典类:遵循深度优先

**注意:**无论是深度优先或广度优先,都是只能继承两个类的情况


# 广度优先

广度优先 : 一条路走到倒数第二级,判断,如果其他路能走到终点,则返回走另一条路.如果不能,则走到终点.

广度优先是由Python内部算法来计算的

class A:
    def so(self):
        print(A)

class B(A):
    pass
    def so(self):
        print(B)

class C(A):
    pass
    def so(self):
        print(C)

class D(B):
    pass
    def so(self):
        print(D)

class E(B):
    pass
    def so(self):
        print(E)

class F(C):
    pass
    def so(self):
        print(F)

class T(C):
    pass
    def so(self):
        print(T)

class L(D,F):
    pass
    def so(self):
        print(L)

class O(E,T):
    pass
    def so(self):
        print(O)

class Q(L,O):
    pass

print(Q.so(1))

​ 以上实例,可以拿这个,来测试,到那个类就把那个类的方法注释了,在看看下次到那个类


# 使用mro()函数来查看广度优先顺序

可以使用mro()函数来查看新式类的广度优先顺序

print(Q.mro())

执行结果:
[<class '__main__.Q'>, <class '__main__.L'>, <class '__main__.D'>, <class '__main__.O'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.T'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

​ 以上实例,执行的代码是基于上面实例的代码来测


# 深度优先

深度优先 : 一条路走到底

就是会从第一个父类中及向上查询,直到查不到才会向第二个父类查询等

因为深度优先是要在python2才能测试实现,目前的环境只有python3,所以测试环节略过